package biolabs.query;

import biolabs.core.BioObject;

/**
 * Represents a predicate over a bio-object.
 * Instances should be immutable.
 *
 * @author Tomas Mikula
 */
public interface Predicate {

	public abstract boolean eval(BioObject obj);


	public static final Predicate TRUE = new Predicate() {
		@Override
		public boolean eval(BioObject obj) {
			return true;
		}
	};

	public static final Predicate FALSE = new Predicate() {
		@Override
		public boolean eval(BioObject obj) {
			return false;
		}
	};

	public static class And implements Predicate {

		private final Predicate p, q;

		public And(Predicate p, Predicate q) {
			this.p = p;
			this.q = q;
		}

		@Override
		public boolean eval(BioObject obj) {
			return p.eval(obj) && q.eval(obj);
		}

	}

	public static class Or implements Predicate {

		private final Predicate p, q;

		public Or(Predicate p, Predicate q) {
			this.p = p;
			this.q = q;
		}

		@Override
		public boolean eval(BioObject obj) {
			return p.eval(obj) || q.eval(obj);
		}
	}

	public static class Not implements Predicate {

		private final Predicate p;

		public Not(Predicate p) {
			this.p = p;
		}

		@Override
		public boolean eval(BioObject obj) {
			return !p.eval(obj);
		}
	}

	public static class Imply implements Predicate {

		private final Predicate p, q;

		public Imply(Predicate p, Predicate q) {
			this.p = p;
			this.q = q;
		}

		@Override
		public boolean eval(BioObject obj) {
			return !p.eval(obj) || q.eval(obj);
		}
	}

	public static class Equiv implements Predicate {

		private final Predicate p, q;

		public Equiv(Predicate p, Predicate q) {
			this.p = p;
			this.q = q;
		}

		@Override
		public boolean eval(BioObject obj) {
			return p.eval(obj) == q.eval(obj);
		}
	}

}
